package cc.shacocloud.mirage.restful;

import cc.shacocloud.mirage.restful.http.HttpHeaderMap;
import io.vertx.core.AsyncResult;
import io.vertx.core.Handler;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.http.Cookie;
import io.vertx.core.http.HttpServerResponse;
import io.vertx.ext.web.Session;
import org.jetbrains.annotations.Nullable;

/**
 * vertx http服务响应对象，可以直接定义在控制器的方法上注入
 */
public interface HttpResponse {
    
    /**
     * @return 当前的路由上下文
     */
    RoutingContext context();
    
    /**
     * @return HTTP请求对象
     */
    HttpRequest request();
    
    /**
     * @return 为响应体写入的总字节数。
     */
    long bytesWritten();
    
    /**
     * 返回当前响应的标题，在{@link HttpServerResponse#headers()} 上的拓展
     */
    HttpHeaderMap headers();
    
    /**
     * 设置一个 HTTP 标头，如果历史存在将被覆盖
     */
    HttpServerResponse setHeader(String name, String value);
    
    /**
     * 设置一个 HTTP 标头，如果历史存在将被覆盖
     */
    HttpServerResponse setHeader(CharSequence name, CharSequence value);
    
    /**
     * 设置一个 HTTP 标头，如果历史存在将被覆盖
     */
    HttpServerResponse setHeader(String name, Iterable<String> values);
    
    /**
     * 设置一个 HTTP 标头，如果历史存在将被覆盖
     */
    HttpServerResponse setHeader(CharSequence name, Iterable<CharSequence> values);
    
    /**
     * 添加一个cookie。这将在响应中发送回客户端。
     *
     * @param cookie Cookie
     * @return 引用，这样API就可以流畅地使用
     */
    HttpServerResponse addCookie(Cookie cookie);
    
    /**
     * 使cookie过期
     *
     * @param name cookie的名称
     * @return cookie(如果存在的话)或null
     */
    @Nullable
    default Cookie removeCookie(String name) {
        return removeCookie(name, true);
    }
    
    /**
     * 从cookie集合中删除一个cookie。如果 {@code invalidate}为{@code true}，那么它将使cookie过期。
     *
     * @param name 饼干的名字
     * @return cookie(如果存在的话)或null
     */
    @Nullable
    Cookie removeCookie(String name, boolean invalidate);
    
    /**
     * @return 会话，由cookie维护
     */
    Session session();
    
    /**
     * @return 获取设置的状态码
     * @see #setStatusCode(int)
     */
    int getStatusCode();
    
    /**
     * 设置状态码。如果没有显式地设置状态消息，则会查找和使用与代码对应的默认状态消息
     *
     * @return 引用，这样API就可以流畅地使用
     */
    HttpResponse setStatusCode(int statusCode);
    
    /**
     * @return 获取设置的状态消息
     * @see #setStatusMessage(String)
     */
    String getStatusMessage();
    
    /**
     * 设置状态消息
     *
     * @return 引用，这样API就可以流畅地使用
     */
    HttpResponse setStatusMessage(String statusMessage);
    
    /**
     * @return 响应是否已经结束
     */
    boolean ended();
    
    
    /**
     * @return 与请求对应的底层 TCP 连接是否已关闭？
     */
    boolean closed();
    
    /**
     * @return 响应的标头是否已写入？
     */
    boolean headWritten();
    
    /**
     * 结束响应
     * <p>
     * 一旦响应结束，就不能再使用它了。
     */
    void end();
    
    /**
     * 结束响应
     * <p>
     * 一旦响应结束，就不能再使用它了。
     */
    void end(String data);
    
    /**
     * 结束响应
     * <p>
     * 一旦响应结束，就不能再使用它了。
     */
    void end(Buffer data);
    
    /**
     * 结束响应，一旦响应结束，就不能再使用它了。
     * <p>
     * 与{@link #end(Buffer)}相同，但在操作完成时调用{@code Handler}
     */
    void end(Buffer data, Handler<AsyncResult<Void>> handler);
    
    /**
     * 与{@link #write(Buffer)}相同
     */
    HttpResponse write(String data);
    
    /**
     * 将一些数据写入流。数据被放在一个内部写队列上，写操作实际上是异步发生的。
     * <p>
     * 如果不使用HTTP分块编码，则在发送任何数据之前，必须将{@code Content-Length}头设置为消息体的总大小。
     *
     * @param data 要写入的数据
     * @return 引用，这样API就可以流畅地使用
     */
    HttpResponse write(Buffer data);
    
    /**
     * 与{@link #write(Buffer)}相同，但在操作完成时调用{@code Handler}
     */
    HttpResponse write(Buffer data, Handler<AsyncResult<Void>> handler);
    
    /**
     * #sendFile(String, long)
     */
    default HttpResponse sendFile(String filename) {
        return sendFile(filename, 0);
    }
    
    /**
     * @see #sendFile(String, long, long)
     */
    default HttpResponse sendFile(String filename, long offset) {
        return sendFile(filename, offset, Long.MAX_VALUE);
    }
    
    /**
     * 要求操作系统将指定为{@code filename}的文件从磁盘直接流到传出连接，完全绕过用户空间(底层操作系统支持的地方)。
     * <p>
     * 实际的服务是异步的，可能要在此方法返回后一段时间才能完成。
     *
     * @param filename 要服务的文件的路径
     * @param offset   偏移量
     * @param length   长度
     * @return 引用，这样API就可以流畅地使用
     */
    HttpResponse sendFile(String filename, long offset, long length);
    
    /**
     * @see #sendFile(String, long, Handler)
     */
    default HttpResponse sendFile(String filename, Handler<AsyncResult<Void>> resultHandler) {
        return sendFile(filename, 0, resultHandler);
    }
    
    /**
     * @see #sendFile(String, long, long, Handler)
     */
    default HttpResponse sendFile(String filename, long offset, Handler<AsyncResult<Void>> resultHandler) {
        return sendFile(filename, offset, Long.MAX_VALUE, resultHandler);
    }
    
    /**
     * 像{@link #sendFile(String, long, long)}，但是提供了一个处理程序，该处理程序将在文件被完全写入网络后被通知。
     *
     * @param filename      要服务的文件的路径
     * @param offset        偏移量
     * @param length        长度
     * @param resultHandler 处理程序，将在完成时调用
     * @return 引用，这样API就可以流畅地使用
     */
    HttpResponse sendFile(String filename, long offset, long length, Handler<AsyncResult<Void>> resultHandler);
    
    
    /**
     * 提供一个处理程序，该处理程序将在主体的最后一部分写入网络后调用。
     * 当客户端接收到响应时，处理程序被异步调用。
     * 这提供了一个钩子，一旦请求已经通过线路发送,允许你做更多的操作，例如资源清理。
     *
     * @param handler 处理程序
     * @return 引用，这样API就可以流畅地使用
     */
    HttpResponse bodyEndHandler(@Nullable Handler<Void> handler);
    
    /**
     * 获取源对象 {@link HttpServerResponse}
     */
    HttpServerResponse getSource();
    
}
